// 纯语言表达:
// 浅拷贝: 只是复制了对象属性或数组元素本身(只是引用地址值)
// 深拷贝: 不仅复制了对象属性或数组元素本身, 还复制了指向的对象(使用递归)
// 举例说明: 拷贝persons数组(多个人对象的数组)
// 浅拷贝: 只是拷贝了每个person对象的引用地址值, 每个person对象只有一份
// 深拷贝: 每个person对象也被复制了一份新的

/**
 * * 浅拷贝，es6的方式实现
 * @param {*} target 
 * @returns 
 */
export function clone1(target) {
  // 类型判断 { } []
  if (typeof target === 'object' && target !== null) {
    if (Array.isArray(target)) {
      return [...target]
    } else {
      return {...target}
    }
  } else {
    return target
  }
}

/**
 * * 浅拷贝，es5的方式实现
 * @param {*} target 
 * @returns 
 */
export function clone2(target) {
  if (typeof target === 'object' && target !== null) {
    // 根据target类型，创建一个容器
    const result = Array.isArray(target) ? [] : {};
    // 遍历 target 数据，使用 for..in.. 适用于数组和对象的方式
    for (let key in target) {
      // 判断当前对象身上是否包含该属性
      if (target.hasOwnProperty(key)) {
        // 将属性设置到 result 结果数据上
        result[key] = target[key];
      }
    }
    return result;
  } else {

  }
}


// ================================================================
//*实现一: 大众乞丐版
// 问题1: 函数属性会丢失
// 问题2: 循环引用会出错
//?不能克隆方法、不能解决循环引用的问题

//*实现二: 面试基础版
// 解决问题1: 函数属性还没丢失
//?可以克隆方法，但不能解决循环引用的问题

//*实现三: 面试加强版本
//解决问题2: 循环引用正常
//?解决循环引用问题，但循环效率较低

//*实现四: 面试加强版本2(优化遍历性能)
// 数组: while | for | forEach() 优于 for-in | keys()&forEach()
// 对象: for-in 与 keys()&forEach() 差不多
//?解决循环效率问题

/**
 * * 实现一: 使用 JSON
 * @param {*} target 
 * @returns 
 */
export function deepClone1(target) {
  // 通过数据创建 JSON 格式的字符串
  let str = JSON.stringify(target);
  // 将 JSON 字符串创建为 JS 数据
  let data = JSON.parse(str);
  return data;
}

/**
 * * 实现二: 使用递归
 * @param {*} target 
 */
export function deepClone2(target) {
  // 检测数据类型
  if (typeof target === 'object' && target !== null) {
    // 创建一个容器
    const result = Array.isArray(target) ? [] : {};
    // 遍历对象
    for (let key in target) {
      // 检测该属性是否为对象本身的属性（不能拷贝原型对象的属性）
      if (target.hasOwnProperty(key)) {
        // 开始拷贝
        result[key] = deepClone2(target[key])
      }
    }
    return result
  } else {
    return target
  }
}

/**
 * * 实现三: 处理循环引用的问题
 * @param {*} target 
 */
export function deepClone3(target, map=new Map()) {
  // 检测数据类型
  if (typeof target === 'object' && target !== null) {
    // 克隆数据之前，判断当前数据是否克隆过
    let cache = map.get(target)
    if (cache) return cache;
    // 创建一个容器
    const result = Array.isArray(target) ? [] : {};
    // 将新值的结果存入到容器中
    map.set(target, result)
    // 遍历对象
    for (let key in target) {
      // 检测该属性是否为对象本身的属性（不能拷贝原型对象的属性）
      if (target.hasOwnProperty(key)) {
        // 开始拷贝
        result[key] = deepClone3(target[key], map)
      }
    }
    return result
  } else {
    return target
  }
}

/**
 * * 实现四: 循环效率问题优化
 * @param {*} target 
 */
 export function deepClone4(target, map=new Map()) {
  // 检测数据类型
  if (typeof target === 'object' && target !== null) {
    // 克隆数据之前，判断当前数据是否克隆过
    let cache = map.get(target)
    if (cache) return cache;
    // 判断目标数据的类型
    let isArray = Array.isArray(target)
    // 创建一个容器
    const result = isArray ? [] : {};
    // 将新值的结果存入到容器中
    map.set(target, result)
    // 遍历对象
    // * 优化：如果目标数据为数组
    if (isArray) {
      target.forEach((item, index) => {
        result[index] = deepClone4(item, map)
      })
    } else {
      // 如果是对象
      Object.keys(target).forEach(key => {
        result[key] = deepClone4(target[key], map)
      })
    }
    return result
  } else {
    return target
  }
}